/*
 * narrow-bridge.c
 *
 *  Created on: 30.04.2011
 *      Author: dkurniawan
 */

#include <stdio.h>
#include "threads/thread.h"
#include "devices/timer.h"
#include <random.h>

#define LEFT 0
#define RIGHT 1
#define EM 1
#define NORMAL 0

// Semaphore for protecting the critical section
static struct semaphore mutex;

// Semaphore for condition synchronization
static struct semaphore okForRightEmergency, okForLeftEmergency, okForLeftNormal, okForRightNormal;

// state variables:
static int ACL = 0;	/* Active car left side */
static int ACR = 0;	/* Active car right side */
static int WCL = 0;	/* Waiting car left side */
static int WCR = 0;	/* Waiting car right side */
static int AEL = 0;	/* Active emergency left side */
static int AER = 0;	/* Active emergency right side */
static int WEL = 0;	/* Waiting emergency left side */
static int WER = 0;	/* Waiting emergency right side */

static void ArriveBridge(int direc, int prio)
{
	sema_down(&mutex);

	//if the car coming from LEFT direction
	if(direc == LEFT){
		// Priority = emergency
		if(prio == EM){
		
			// Check whether the bridge already accupied with 3 vehicles
			// AND whether there is car comming from another direction
			// If true, then give the bridge.			
			if((ACL + AEL < 3) && (ACR + AER) == 0){
				sema_up(&okForLeftEmergency);
				AEL++;
			} 
			// Else the emergency car needs to wait
			else {
				WEL++;
			}
			sema_up(&mutex);
			sema_down(&okForLeftEmergency);
		}
		// Priority = normal
		else {
			// Check whether the bridge already accupied with 3 vehicles
			// AND whether there is car comming from another direction
			// AND whether there is any Emergency car waiting
			// If true, then give the bridge.			
			if((ACL + AEL < 3) && (WEL + WER) == 0 && (ACR + AER) == 0){
				sema_up(&okForLeftNormal);
				ACL++;
			} 
			// Else the normal car needs to wait
			else {
				WCL++;
			}
			sema_up(&mutex);
			sema_down(&okForLeftNormal);
		}

	}
	//Direction RIGHT
	else {
		// Priority = emergency
		if(prio == EM){
			// Check whether the bridge already accupied with 3 vehicles
			// AND whether there is car comming from another direction
			// If true, then give the bridge.
			if((ACR + AER < 3) && (ACL + AEL) == 0){
				sema_up(&okForRightEmergency);
				AER++;
			} 
			// Else the emergency car needs to wait
			else {
				WER++;
			}
			sema_up(&mutex);
			sema_down(&okForRightEmergency);
		}
		// Priority = normal;
		else {			
			// Check whether the bridge already accupied with 3 vehicles
			// AND whether there is car comming from another direction
			// AND whether there is any Emergency car waiting
			// If true, then give the bridge.
			if((ACR + AER < 3) && (WEL + WER) == 0 && (ACL + AEL) == 0){
				sema_up(&okForRightNormal);
				ACR++;
			} 
			// Else the normal car needs to wait
			else {
				WCR++;
			}
			sema_up(&mutex);
			sema_down(&okForRightNormal);
		}
	}

}

static void ExitBridge(int direc, int prio)
{
	sema_down(&mutex);
	// First check the car direction
	if(direc == LEFT){
		// Since the car is already exit the bridge, substract the Active car by 1 according to priority.
		if(prio == EM){
			AEL--;
		}
		else{
			ACL--;
		}
		
		// The car which already exit needs to signal the other car which are waiting for the bridge
		// If Emergency car there are any Emergency Car from Left, signal them
		if(WEL > 0){
			sema_up(&okForLeftEmergency);
			WEL--;
			AEL++;
		} 
		// If there are any emergency car waiting from RIGHT 
		// AND no car cross the bridge from LEFT
		// signal them
		else if(WEL == 0 && WER > 0 && ACL + AEL == 0){
			while(WER > 0 && AER + ACR < 3){			
				sema_up(&okForRightEmergency);
				WER--;
				AER++;
			}
			while(WCR > 0 && AER + ACR < 3){
				sema_up(&okForRightNormal);
				ACR++;
				WCR--;
			}
		} 
		// If there are no Emergency Car waiting
		// Give the bridge to normal car
		else if (WCL > 0 && WER + WEL == 0){
			while(WCL > 0 && ACL + AEL < 3){			
				sema_up(&okForLeftNormal);
				WCL--;
				ACL++;
			}
		} 
		// If there are no more waitng normal car from this direction
		// Give the bridge to other direction
		else if (WCL == 0 && WER + WEL == 0 && ACL + AEL == 0) {
			while(WCR > 0 && ACR + AER < 3) {			
				sema_up(&okForRightNormal);
				ACR++;
				WCR--;
			}
		}

	} else {
		if(prio == EM){
			AER--;
		}
		else{
			ACR--;
		}

		if(WER > 0){
			sema_up(&okForRightEmergency);
			WER--;
			AER++;
		} else if(WER == 0 && WEL > 0 && ACR + AER == 0){
			while(WEL > 0 && AEL + ACL < 3){			
				sema_up(&okForLeftEmergency);
				WEL--;
				AEL++;
			}
			
			while(WCL > 0 && AEL + ACL < 3){
				sema_up(&okForLeftNormal);
				ACL++;
				WCL--;
			}
			
		} else if (WCR > 0 && WER + WEL == 0){
			while(WCR > 0 && ACR + AER < 3){			
				sema_up(&okForRightNormal);
				WCR--;
				ACR++;
			}
		} else if (WCR == 0 && WER + WEL == 0 && ACR + AER == 0) {
			while(WCL > 0 && ACL + AEL < 3){			
				sema_up(&okForLeftNormal);
				ACL++;
				WCL--;
			}
		}

	}
	sema_up(&mutex);
}

static void CrossBridge(int direc,int prio)
{
	printf("%s Going ON the bridge -> direc: %i prio: %i\n",thread_current()->name, direc, prio);
	timer_sleep ((random_ulong () % 10) + 5);
	printf("%s Going OUT the bridge -> direc: %i prio: %i\n",thread_current()->name, direc, prio);
}

/* The process of crossing over the narrow bridge */
static void OneVehicle (int direc, int prio)
{
	ArriveBridge (direc, prio);
	CrossBridge (direc, prio);
	ExitBridge (direc, prio);
}

static void CreateAverageCarLeft(void *aux) {
	OneVehicle(0,0);
}

static void CreateEmergencyCarLeft(void *aux) {
	OneVehicle(0,1);
}

static void CreateAverageCarRight(void *aux) {
	OneVehicle(1,0);
}

static void CreateEmergencyCarRight(void *aux) {
	OneVehicle(1,1);
}

void start_narrow_bridge(unsigned int num_vehicles_left, unsigned int num_vehicles_right,
        unsigned int num_emergency_left, unsigned int num_emergency_right)
{

	sema_init(&mutex, 1);
	sema_init(&okForLeftEmergency, 0);
	sema_init(&okForLeftNormal, 0);
	sema_init(&okForRightEmergency, 0);
	sema_init(&okForRightNormal, 0);

	printf("Normal Left %d, Normal Right %d, Emergency Left %d, Emergency Right %d\n=======================================\n",
    		num_vehicles_left, num_vehicles_right,
    	        num_emergency_left, num_emergency_right);
	unsigned int i;
	char buf[16];

	for(i = 0; i < num_emergency_left; i++) {
		snprintf(buf, 16, "ECL[%d]", i);
		thread_create(buf, 0, CreateEmergencyCarLeft, 0);
	}

	for(i = 0; i < num_vehicles_left; i++) {
		snprintf(buf, 16, "ACL[%d]", i);
		thread_create(buf, 0, CreateAverageCarLeft, 0);
	}

	for(i = 0; i < num_emergency_right; i++) {
		snprintf(buf, 16, "ECR[%d]", i);
		thread_create(buf, 0, CreateEmergencyCarRight, 0);
	}

	for(i = 0; i < num_vehicles_right; i++) {
		snprintf(buf, 16, "ACR[%d]", i);
		thread_create(buf, 0, CreateAverageCarRight, 0);
	}
}


